package com.leyou.search.service;

import com.leyou.item.api.SpecApi;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.pojo.Brand;
import com.leyou.item.pojo.Category;
import com.leyou.item.pojo.SpecParam;
import com.leyou.search.clients.BrandClient;
import com.leyou.search.clients.CategoryClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.utils.SearchRequest;
import com.leyou.search.utils.SearchResult;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: taft
 * @Date: 2018-12-26 10:03
 */
@Service
public class SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private SpecApi specApi;


    public SearchResult xxx(SearchRequest searchRequest) {

        String key = searchRequest.getKey();

        //没有查询条件不执行查询
        if (!StringUtils.isNotBlank(key)) {
            return null;
        }

        //封装查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        // 通过sourceFilter设置返回的结果字段,我们只需要id、skus、subTitle
        queryBuilder.withSourceFilter(new FetchSourceFilter(
                new String[]{"id", "skus", "subTitle"}, null));

        //根据查询条件参数对象创建一个查询对象
        QueryBuilder query = buildBasicQueryWithFilter(searchRequest);

        queryBuilder.withQuery(query);


        Integer page = searchRequest.getPage();

        if (null == page) {
            page = SearchRequest.DEFAULT_PAGE;
        }
        //添加分页条件，JPA的分页从0开始所以一定要-1
        queryBuilder.withPageable(PageRequest.of(page - 1, SearchRequest.DEFAULT_SIZE));

        // 1.3、聚合
        String categoryAggName = "category"; // 商品分类聚合名称
        String brandAggName = "brand"; // 品牌聚合名称


        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));

        //执行查询获取分页结果
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

        //根据分类聚合名称获取聚合结果
        List<Long> categoryIds = new ArrayList<>();
        LongTerms categoryAgg = (LongTerms) search.getAggregation(categoryAggName);
        List<LongTerms.Bucket> categoryBuckets = categoryAgg.getBuckets();
        for (LongTerms.Bucket categoryBucket : categoryBuckets) {
            categoryIds.add(categoryBucket.getKeyAsNumber().longValue());
        }

        //根据品牌聚合名称获取聚合结果
        List<Long> brandIds = new ArrayList<>();
        LongTerms brandAgg = (LongTerms) search.getAggregation(brandAggName);

        List<LongTerms.Bucket> brandBuckets = brandAgg.getBuckets();

        for (LongTerms.Bucket brandBucket : brandBuckets) {
            brandIds.add(brandBucket.getKeyAsNumber().longValue());
        }


        List<Category> categories = new ArrayList<>();

        //根据分类id查询到的所有的分类的名称
        List<String> names = this.categoryClient.queryNameByIds(categoryIds);

        //根据分类的名称以及分类的id动态组合分类对象
        for (int i = 0; i < categoryIds.size(); i++) {
            Category category = new Category();
            category.setId(categoryIds.get(i));
            category.setName(names.get(i));
            categories.add(category);
        }


        //根据品牌的id查询所有的品牌对象
        List<Brand> brands = this.brandClient.getBrandById(brandIds);

        List<Map<String, Object>> specs = null; // 规格参数过滤条件

        if (1 == categoryIds.size()) {
            //当分类只有一个时，根据分类的id去查询当前分类下所有的可搜索规格参数的聚合
            specs = getSpecs(categoryIds.get(0), query);
        }

        return new SearchResult(search.getTotalElements(), new Long(search.getTotalPages()), search.getContent(), categories, brands, specs);
    }


    //规格参数的聚合，不但要根据分类来，还要根据查询条件而来，比如点击了cpu品牌为海思，就不应该展示其他信息
    private List<Map<String, Object>> getSpecs(Long cid, QueryBuilder query) {

        List<Map<String, Object>> specs = new ArrayList<>();

        //查询所有的可搜索规格字段
        List<SpecParam> specParams = this.specApi.querySpecParam(null, cid, true, null);

        //对所有的可搜索规格字段进行聚合
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //不仅要聚合规格参数还要对其进行查询过滤
        queryBuilder.withQuery(query);

        //循环添加聚合条件
        specParams.forEach(specParam -> {
            //聚合字段的部分名称,以及聚合的名称
            String name = specParam.getName();//
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs." + name + ".keyword"));
        });

        //执行查询
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());


        //循环解析聚合结果
        specParams.forEach(specParam -> {

            Map<String, Object> specMap = new HashMap<>();
            //聚合的名称
            String name = specParam.getName();//
            //根据聚合的名称来获取当前聚合的结果
            StringTerms terms = (StringTerms) search.getAggregation(name);

            List<StringTerms.Bucket> buckets = terms.getBuckets();

            List<String> names = new ArrayList<>();
            for (StringTerms.Bucket bucket : buckets) {
                String specName = bucket.getKeyAsString();
                names.add(specName);
            }
            specMap.put("k", name);
            specMap.put("options", names);

            specs.add(specMap);
        });


        return specs;
    }


    // 构建基本查询条件
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 基本查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));
        // 过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        // 整理过滤条件
        Map<String, String> filter = request.getFilter();

        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 商品分类和品牌已经聚合
            if (key != "cid3" && key != "brandId") {
                key = "specs." + key + ".keyword";
            }
            // 字符串类型，进行term查询
            filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
        }
        // 添加过滤条件
        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;
    }
}
